package com.zmytest.common;

import com.zmytest.annotation.ParamName;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.security.InvalidParameterException;
import java.util.*;

/**
 * Created on 2018/12/13.
 *  自定义配置规则
 *  配合spring aop使用
 * @author zhangmingyu
 */
public class ConfigStringAnalyzer {
    private static final String INPUT_ARGS_SIGN = "$IN";
    private static final String OUTPUT_RESULT_SIGN = "$OUT";
    /**
     * 主要访问的方法
     * @param joinPoint 可以通过joinPoint获取到入参Map
     * @param result result是aop切入的方法返回结果
     * @param configStr 配置的字符串
     * @return 解析结果
     */
    public static Object getValue(JoinPoint joinPoint, Object result, String configStr){
        if (configStr.startsWith(INPUT_ARGS_SIGN)) {
            Map<String,Object> inputArgsMap = getInputArgsMap(joinPoint);
            List<String> configs = getConfigsWithoutSign(configStr);
            if (configs.size() == 0) {
                return inputArgsMap;
            }else {
                String inputArgName = configs.get(0);
                Object inputArg = inputArgsMap.get(inputArgName);
                configs.remove(0);
                return getValue(inputArg,configs);
            }
        } else if (configStr.startsWith(OUTPUT_RESULT_SIGN)){
            List<String> configs = getConfigsWithoutSign(configStr);
            if (configs.size() == 0) {
                return result;
            }else {
                return getValue(result,configs);
            }
        } else {
            return configStr;
        }
    }

    private static List<String> getConfigsWithoutSign(String configStr){
        List<String> configs = new LinkedList<>();
        configs.addAll(Arrays.asList(configStr.split("\\.")));
        configs.remove(0);
        return configs;
    }

    /**
     * 以baseObj为最外层，根据fieldOrMethodNames，逐步深入找到要使用的field或无参方法。
     * 优化递归，改为for循环。
     * @param baseObj 最外层基类
     * @param fieldOrMethodNames 属性名或无参方法名
     * @return 最终结果对象
     */
    private static Object getValue(Object baseObj,List<String> fieldOrMethodNames){
        Object value = baseObj;
        for (String fieldOrMethodName : fieldOrMethodNames) {
            //判断是无参方法、还是属性（field）
            if (fieldOrMethodName.endsWith("()")) {
                try {
                    String methodName = fieldOrMethodName.substring(0, fieldOrMethodName.length() - 2);
                    Method method = value.getClass().getMethod(methodName);
                    method.setAccessible(true);
                    value = method.invoke(value);
                } catch (NoSuchMethodException e) {
                    throw new InvalidParameterException("method[" + fieldOrMethodName + "]不正确，不能根据名称获取到该方法");
                } catch (IllegalAccessException e) {
                    throw new InvalidParameterException("method[" + fieldOrMethodName + "]，赋值访问权限失败");
                } catch (InvocationTargetException e) {
                    throw new InvalidParameterException("method[" + fieldOrMethodName + "]，不能调用该方法");
                }
            } else {
                try {
                    Field field = value.getClass().getDeclaredField(fieldOrMethodName);
                    field.setAccessible(true);
                    value = field.get(value);
                } catch (NoSuchFieldException e) {
                    throw new InvalidParameterException("field[" + fieldOrMethodName + "]不正确,找不到改名字的field");
                } catch (IllegalAccessException e) {
                    throw new InvalidParameterException("field[" + fieldOrMethodName + "]，无法获取值");
                }
            }
        }
        return value;
    }

    private static Map<String,Object> getInputArgsMap(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        Parameter[] parameters =methodSignature.getMethod().getParameters();
        for (Parameter parameter : parameters){
            ParamName paramName = parameter.getAnnotation(ParamName.class);
            String value = paramName.value();
        }
        String[] argNames = methodSignature.getParameterNames();
//        String[] argNames = getFieldsName(joinPoint.getTarget().getClass().getName(),joinPoint.getSignature().getName());
        Map<String,Object> result = new HashMap<>();
        for (int i =0;i<argNames.length;i++){
            result.put(argNames[i],args[i]);
        }
        return result;
    }

    private static String[] getParameterNames(){
        return null;
    }
}
